package com.yc.mybatis.core;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SqlSession {
	private ConnectionPool connectionPool;
	private Map<String, MapperInfo> mapperInfos;
	
	public SqlSession(ConnectionPool connectionPool, Map<String, MapperInfo> mapperInfos) {
		this.connectionPool = connectionPool;
		this.mapperInfos = mapperInfos;
	}

	//泛型方法返回值前必须带一个<T>，这是一种约定，表示该方法是泛型方法.
	public <T> List<T> selectList(String sqlId) {
		return this.selectList(sqlId, null);
	}

	public <T> List<T> selectList(String sqlId, Object obj) {
		if(mapperInfos == null || mapperInfos.isEmpty()) {
			throw new RuntimeException("没有读取到映射文件信息。。。");
		}
		
		MapperInfo mapperInfo = mapperInfos.getOrDefault(sqlId, null);
		if(mapperInfo == null) {
			throw new RuntimeException("找不到您要执行的" + sqlId + "....");
		}
		
		Connection con = null;
		
		try {
			con = connectionPool.getConnection(); // 从连接池中获取一个可用的连接
			//System.out.println(con);
			String sql = mapperInfo.getSql(); // 获取sql语句
			//System.out.println(sql);
			
			List<String> params = mapperInfo.getParams();
			//System.out.println(params);
			
			List<Object> paramsValue = null; // 占位符的值
			if(params != null && !params.isEmpty()) { // 说明sql语句中有占位符
				paramsValue = this.getParamsValue(params, mapperInfo.getParameterType(), obj);
			}
			
			DBUtil db = new DBUtil();
			String resultType = mapperInfo.getResultType();
			//System.out.println(resultType);
			
			if("map".equalsIgnoreCase(resultType)) {
				return (List<T>) db.findList(con, sql, paramsValue);
			}
			Class<?> clazz = Class.forName(resultType); // 先加载这个类
			return db.findList(con, clazz, sql, paramsValue);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(con != null) {
				this.close(con);
			}
		}
		return null;
	}

	public int insert(String sqlId, Object obj) {
		return this.update(sqlId, obj);
	}
	
	public int insert(String sqlId) {
		return this.update(sqlId, null);
	}

	public int update(String sqlId) {
		return this.update(sqlId, null);
	}

	public int delete(String sqlId) {
		return this.update(sqlId, null);
	}

	public int delete(String sqlId, Object obj) {
		return this.update(sqlId, obj);
	}

	public <T> T selectOne(String sqlId, Object obj) {
		if(mapperInfos == null || mapperInfos.isEmpty()) {
			throw new RuntimeException("没有读取到映射文件信息。。。");
		}
		
		MapperInfo mapperInfo = mapperInfos.getOrDefault(sqlId, null);
		if(mapperInfo == null) {
			throw new RuntimeException("找不到您要执行的" + sqlId + "....");
		}
		
		Connection con = null;
		
		try {
			con = connectionPool.getConnection(); // 从连接池中获取一个可用的连接
			String sql = mapperInfo.getSql(); // 获取sql语句
			List<String> params = mapperInfo.getParams();
			List<Object> paramsValue = null; // 占位符的值
			if(params != null && !params.isEmpty()) { // 说明sql语句中有占位符
				paramsValue = this.getParamsValue(params, mapperInfo.getParameterType(), obj);
				System.out.println(paramsValue);
			}
			
			DBUtil db = new DBUtil();
			String resultType = mapperInfo.getResultType();
			
			if("int".equalsIgnoreCase(resultType)) {
				return (T) db.total(con, sql, paramsValue);
			}
			
			if("map".equalsIgnoreCase(resultType)) {
				return (T) db.findOne(con, sql, paramsValue);
			}
			Class<?> clazz = Class.forName(resultType); // 先加载这个类
			return db.findOne(con, clazz, sql, paramsValue);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(con != null) {
				this.close(con);
			}
		}
		return null;
	}

	public <T> T selectOne(String sqlId) {
		return null;
	}

	/**
	 * 根据用户给定的参数类型从用户给定的对象中取出占位符的值
	 * @param params
	 * @param parameterType
	 * @param obj
	 * @return
	 */
	private List<Object> getParamsValue(List<String> params, String parameterType, Object obj) throws Exception{
		if(params == null || params.isEmpty() || obj == null || StringUtil.checkNull(parameterType)) {
			return Collections.emptyList();
		}
		
		List<Object> vals = new ArrayList<Object>();
		
		if("int".equalsIgnoreCase(parameterType) || "float".equalsIgnoreCase(parameterType) || "double".equalsIgnoreCase(parameterType) || 
				"string".equalsIgnoreCase(parameterType)) {;
				vals.add(obj);
				return vals;
		}
		
		if("map".equalsIgnoreCase(parameterType)){ // 说明是map类型
			Map<String, Object> map = (Map<String, Object>) obj;
			for(String key : params) {
				vals.add(map.get(key));
			}
			return vals;
		}
		
		//用户自定义的对象
		Class<?> clazz = Class.forName(parameterType); // 先加载这个类
		
		// 获取这个类中的getter方法 -> 获取这个类的所有方法
		Method[] methods = clazz.getDeclaredMethods();
		if(methods == null || methods.length <= 0) {
			return vals;
		}
		
		//从这个方法中找出getter方法
		Map<String, Method> getters = new HashMap<String, Method>();
		String methodName = null;
		for(Method method : methods) { // 遍历所有的方法
			methodName = method.getName(); // 获取方法的方法名
			if(methodName.startsWith("get")) {
				getters.put(methodName, method);
			}
		}
		
		//循环遍历占位符名称，这个名称其实就是用户给定的对象的属性值
		//要通过这些属性的getter方法从用户给定的对象中获取这个属性的值
		Method method = null;
		for(String attrName : params) {
			//规则是属性名第一个字母大写，然后前面加上get
			methodName = "get" + attrName.substring(0, 1).toUpperCase() + attrName.substring(1);
			
			//判断这个属性的getter方法是否存在
			method = getters.get(methodName);
			if(method == null) { // 说明这个属性没有提供对应的getter方法
				vals.add(null); // 那么这个属性的值就没法注入，则说明该属性的值为空
				continue;
			}
			
			//如果有这个方法，则需要激活运行这个方法
			// obj.getGname()
			vals.add(method.invoke(obj));
		}
		return vals;
		
	}
	
	/**
	 * 释放连接给连接池的方法
	 * @param con
	 */
	private void close(Connection con) {
		connectionPool.freeConnection(con);
	}
	
	public int update(String sqlId, Object obj) {
		if(mapperInfos == null || mapperInfos.isEmpty()) {
			throw new RuntimeException("没有读取到映射文件信息。。");
		}
		
		MapperInfo mapperInfo = mapperInfos.getOrDefault(sqlId, null);
		if(mapperInfo == null) {
			throw new RuntimeException("找不到您要执行的" + sqlId + "...");
		}
		
		int result = -1;
		
		Connection con = null;
		try {
			con = connectionPool.getConnection(); // 从连接池中获取一个可用的连接
			//System.out.println(con);
			String sql = mapperInfo.getSql(); // 获取要执行的sql语句
			List<String> params = mapperInfo.getParams(); // 获取指定语句中的占位符
			List<Object> paramsValue = null; // 存放每一个占位符的值
			
			if(params != null && !params.isEmpty()) { // 说明sql语句中有占位符，此时需要从用户给定的对象中，按顺序取出占位符的值
				paramsValue = getParamsValue(params, mapperInfo.getParameterType(), obj);
			}
			
			DBUtil db = new DBUtil();
			result = db.update(con, sql, paramsValue);
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(con != null) {
				this.close(con);
			}
		}
		return result;
	}
}
